/*
 Copyright 2009 Abdulla Abdurakhmanov (abdulla@latestbit.com)
 Original sources are available at www.latestbit.com

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 StdDataTypesCoder.cpp. Created on: 10.06.2009 12:05:25
 */


#include "smartobjects/coders/StdDataTypesCoder.hpp"
#include "smartobjects/coders/StdDataTypesCoderGrammar.hpp"


namespace smartobjects {

StdDataTypesCoder::StdDataTypesCoder() : m_poGrammar(new StdDataTypesCoderGrammar()) {
}

StdDataTypesCoder::~StdDataTypesCoder() {
	m_poGrammar->release();
}

const std::string& StdDataTypesCoder::getAlgorithm() const {
	static const std::string STD_ALGORITHM_NAME = "SSOC";
	return STD_ALGORITHM_NAME;
}

void StdDataTypesCoder::encodeTag(DataTypeTags tag, ByteBuffer* buffer) {
        buffer->append((unsigned char)tag);
}

unsigned char StdDataTypesCoder::decodeTag(ByteBuffer* buffer) {
        unsigned char nTag = buffer->getData()[buffer->getPos()];
        buffer->seek(1);
        return nTag;
}

bool StdDataTypesCoder::checkTag(ByteBuffer* buffer, DataTypeTags type, bool bCannotBeNull) {
        unsigned char nTag = decodeTag(buffer);
        if( nTag!= (unsigned char)type ) {
                if( bCannotBeNull || (nTag != (unsigned char) NULL_TAG ) ) {
                	throw DataTypesCoderException( (boost::format("Incorrect data type received. Found tag: %1%. Expected: %2%") %((unsigned int)nTag) %((unsigned int)type)).str());
                }
                return false;
        }
        else
                return true;
}

void StdDataTypesCoder::encodeFloatingLength(const unsigned int len, ByteBuffer* buffer)  {
	if ( len <= 0x7F ) {
		buffer->append ( (unsigned char) len );
	}
	else
	if ( len <= 0xFF ) {
		buffer->append ( (unsigned char) 0x81 );
		buffer->append ( (unsigned char) len );
	}
	else
	if ( len <= 0xFFFF ) {
		buffer->append ( (unsigned char) 0x82 );
		buffer->append ( (unsigned char) ( len >> 8) );
		buffer->append ( (unsigned char) len );
	}
	else
	if ( len <= 0xFFFFFF ) {
		buffer->append ( (unsigned char) 0x83 );
		buffer->append ( (unsigned char) ( len >> 16) );
		buffer->append ( (unsigned char) ( len >> 8) );
		buffer->append ( (unsigned char) len );
	}
	else {
		buffer->append ( (unsigned char) 0x84 );
		buffer->append ( (unsigned char) ( len >> 24) );
		buffer->append ( (unsigned char) ( len >> 16) );
		buffer->append ( (unsigned char) ( len >> 8) );
		buffer->append ( (unsigned char) len );
	}
}

unsigned int StdDataTypesCoder::decodeFloatingLength(ByteBuffer* buffer)  {
	unsigned char bt = buffer->getData()[buffer->getPos()];
	buffer->seek ( 1 );
	if ( bt <=0x7F ) {
		return bt;
	}
	else {
		int countOfBt= bt&0x0F;
		int result = 0;
		for ( int i= countOfBt; i > 0; i-- ) {
			bt = buffer->getData()[buffer->getPos()];
			buffer->seek ( 1 );
			result <<= 8;
			result|= bt;
		}
		return result;
	}
}

void StdDataTypesCoder::encodeFixedLength(const unsigned int nLen, ByteBuffer* buffer)  {
	unsigned int valueToEncode = OSEndianTools::host_to_network(nLen);
	buffer->append( (unsigned char*)&valueToEncode, sizeof(unsigned int) );
}

unsigned int StdDataTypesCoder::decodeFixedLength ( ByteBuffer* buffer )  {
	unsigned int* valueToDecode = (unsigned int*) buffer->getDataAtPos();
	buffer->seek(sizeof(unsigned int));
	return OSEndianTools::network_to_host( *valueToDecode );
}

void StdDataTypesCoder::encodeByte(const char value, ByteBuffer* buffer) {
	encodeTag ( BYTE_TAG, buffer );
	buffer->append((unsigned char)value);
}

char StdDataTypesCoder::decodeByte(ByteBuffer* buffer)  {
	checkTag(buffer, BYTE_TAG, true);
	char result = (char)buffer->getData()[buffer->getPos()];
	buffer->seek(1);
	return result;
}

void StdDataTypesCoder::encodeUByte(const unsigned char value, ByteBuffer* buffer) {
	encodeTag ( UBYTE_TAG, buffer );
	buffer->append(value);
}

unsigned char StdDataTypesCoder::decodeUByte(ByteBuffer* buffer)  {
	checkTag(buffer, UBYTE_TAG, true);
	unsigned char result = buffer->getData()[buffer->getPos()];
	buffer->seek(1);
	return result;
}

void StdDataTypesCoder::encodeInt16(const short value, ByteBuffer* buffer)  {
	encodeNumberType(value, INT16_TAG, buffer);
}

short StdDataTypesCoder::decodeInt16(ByteBuffer* buffer)  {
	return decodeNumberType<short>(INT16_TAG, buffer);
}

void StdDataTypesCoder::encodeUInt16(const unsigned short value, ByteBuffer* buffer) {
	encodeNumberType(value, UINT16_TAG, buffer);
}

unsigned short StdDataTypesCoder::decodeUInt16(ByteBuffer* buffer) {
	return decodeNumberType<unsigned short>(UINT16_TAG, buffer);
}

void StdDataTypesCoder::encodeInt32(const int value, ByteBuffer* buffer) {
	encodeNumberType(value, INT32_TAG, buffer);
}

int StdDataTypesCoder::decodeInt32(ByteBuffer* buffer) {
	return decodeNumberType<int>(INT32_TAG, buffer);
}

void StdDataTypesCoder::encodeUInt32(const unsigned int value, ByteBuffer* buffer) {
	encodeNumberType(value, UINT32_TAG, buffer);
}

unsigned int StdDataTypesCoder::decodeUInt32(ByteBuffer* buffer) {
	return decodeNumberType<unsigned int>(UINT32_TAG, buffer);
}

void StdDataTypesCoder::encodeInt64(const boost::int64_t value, ByteBuffer* buffer) {
	encodeNumberType(value, INT64_TAG, buffer);
}

boost::int64_t StdDataTypesCoder::decodeInt64(ByteBuffer* buffer) {
	return decodeNumberType<boost::int64_t>(INT64_TAG, buffer);
}

void StdDataTypesCoder::encodeUInt64(const boost::uint64_t value, ByteBuffer* buffer) {
	encodeNumberType(value, UINT64_TAG, buffer);
}

boost::uint64_t StdDataTypesCoder::decodeUInt64(ByteBuffer* buffer) {
	return decodeNumberType<boost::uint64_t>(UINT64_TAG, buffer);
}

void StdDataTypesCoder::encodeFloat(const float value, ByteBuffer* buffer) {
	encodeNumberType(value, FLOAT_TAG, buffer);
}

float StdDataTypesCoder::decodeFloat(ByteBuffer* buffer) {
	return decodeNumberType<float>(FLOAT_TAG, buffer);
}

void StdDataTypesCoder::encodeDouble(const double value, ByteBuffer* buffer) {
	encodeNumberType(value, DOUBLE_TAG, buffer);
}

double StdDataTypesCoder::decodeDouble(ByteBuffer* buffer) {
	return decodeNumberType<double>(DOUBLE_TAG, buffer);
}

void StdDataTypesCoder::encodeBigInteger(const BigInteger& value, ByteBuffer* buffer) {
	encodeTag ( BIGINTEGER_TAG, buffer );
	encodeStringValue ( value.toString(), buffer );
}

void StdDataTypesCoder::decodeBigInteger(BigInteger& toValue, ByteBuffer* buffer) {
	if(checkTag(buffer, BIGINTEGER_TAG)) {
		std::string toString;
		decodeStringValue(toString, buffer);
		toValue = toString;
	}
	else
		toValue = BigInteger::ZERO;
}

void StdDataTypesCoder::encodeBigDecimal(const BigDecimal& value, ByteBuffer* buffer) {
	encodeTag ( BIGDECIMAL_TAG, buffer );
	encodeStringValue ( value.toDecimalFormatString(), buffer );
}

void StdDataTypesCoder::decodeBigDecimal(BigDecimal& toValue, ByteBuffer* buffer) {
	if(checkTag(buffer, BIGDECIMAL_TAG)) {
		std::string toString;
		decodeStringValue(toString, buffer);
		toValue = toString;
	}
	else
		toValue = BigDecimal::ZERO;
}

void StdDataTypesCoder::encodeStringValue (const std::string& value, ByteBuffer* buffer) {
	encodeFloatingLength(value.length(), buffer);
	buffer->append ( (unsigned char*)value.c_str(), value.length() );
}

void StdDataTypesCoder::decodeStringValue (std::string& toString, ByteBuffer* buffer)  {
	toString.clear();
	unsigned int nLen = decodeFloatingLength(buffer);
	if(nLen > buffer->getLenFromPos())
		throw DataTypesCoderException( boost::format( "Data length is out of bounds packet: %1% > %2%.")
			%nLen %buffer->getLenFromPos()
		);

	toString.assign((char*) buffer->getDataAtPos(), nLen);
	buffer->seek(nLen);
}

void StdDataTypesCoder::encodeString(const std::string& value, ByteBuffer* buffer, bool bEmptyAsNull) {
	if(bEmptyAsNull && value.empty()) {
		encodeNull ( buffer );
	}
	else {
		encodeTag ( STRING_TAG, buffer );
		encodeStringValue ( value, buffer );
	}
}

void StdDataTypesCoder::decodeString(std::string& toString, ByteBuffer* buffer) {
	if(checkTag(buffer, STRING_TAG)) {
		decodeStringValue(toString, buffer);
	}
	else
		toString.clear();
}

void StdDataTypesCoder::encodeWStringValue (const std::wstring& value, ByteBuffer* buffer) {
	encodeFloatingLength(value.length() * 2, buffer);
	for(unsigned int i=0; i < value.length(); i++) {
		boost::int16_t ucs2Char = (boost::int16_t)value[i];
		boost::int16_t valueToEncode = OSEndianTools::host_to_network(ucs2Char);
		buffer->append( (unsigned char*)&valueToEncode, sizeof(boost::int16_t) );
	}
}

void StdDataTypesCoder::decodeWStringValue (std::wstring& toString, ByteBuffer* buffer)  {
	toString.clear();
	unsigned int nLen = decodeFloatingLength(buffer);
	if(nLen > buffer->getLenFromPos())
		throw DataTypesCoderException( boost::format( "Data length is out of bounds packet: %1% > %2%.")
			%nLen %buffer->getLenFromPos()
		);

	short* pSource = (short*)buffer->getDataAtPos();
	for(unsigned int i=0; i < nLen / 2; i++) {
		boost::int16_t ucs2Char = OSEndianTools::network_to_host(*(pSource++));
		toString += ucs2Char;
	}
	buffer->seek(nLen);
}
void StdDataTypesCoder::encodeWString(const std::wstring& value, ByteBuffer* buffer, bool bEmptyAsNull) {
	if(bEmptyAsNull && value.empty()) {
		encodeNull ( buffer );
	}
	else {
		encodeTag ( WSTRING_TAG, buffer );
		buffer->append((unsigned char)UCS_2);
		encodeWStringValue ( value, buffer );
	}
}

void StdDataTypesCoder::decodeWString(std::wstring& toString, ByteBuffer* buffer) {
	if(checkTag(buffer, WSTRING_TAG)) {
		unsigned char unicodeEncodingType = *buffer->getDataAtPos();
		buffer->seek(1);
		if(unicodeEncodingType!=UCS_2)
			throw DataTypesCoderException( boost::format( "Unsupported unicode encoding type id %1%.")
				%unicodeEncodingType
			);
		decodeWStringValue(toString, buffer);
	}
	else
		toString.clear();
}

void StdDataTypesCoder::encodeChar(const char value, ByteBuffer* buffer) {
	encodeTag ( CHAR_TAG, buffer );
	buffer->append ( (unsigned char)value );
}

char StdDataTypesCoder::decodeChar(ByteBuffer* buffer) {
	checkTag(buffer, CHAR_TAG, true);
	char result = (char)buffer->getData()[buffer->getPos()];
	buffer->seek(1);
	return result;
}

void StdDataTypesCoder::encodeWChar(const wchar_t value, ByteBuffer* buffer) {
	encodeTag ( WCHAR_TAG, buffer );
	boost::int16_t valueToEncode = OSEndianTools::host_to_network( (boost::int16_t )value);
	buffer->append( (unsigned char*)&valueToEncode, sizeof(boost::int16_t) );
}

wchar_t StdDataTypesCoder::decodeWChar(ByteBuffer* buffer) {
	checkTag(buffer, WCHAR_TAG, true);
	short* pSource = (short*)buffer->getDataAtPos();
	boost::int16_t ucs2Char = OSEndianTools::network_to_host(*pSource);
	buffer->seek(2);
	return (wchar_t)ucs2Char;
}

void StdDataTypesCoder::encodeBoolean(const bool value, ByteBuffer* buffer) {
	encodeTag ( BOOLEAN_TAG, buffer );
	buffer->append ( (int)value );
}

bool StdDataTypesCoder::decodeBoolean(ByteBuffer* buffer) {
	checkTag(buffer, BOOLEAN_TAG, true);
	bool result = (bool)buffer->getData()[buffer->getPos()];
	buffer->seek(1);
	return result;
}

void StdDataTypesCoder::encodeDateTime(const time_t value, ByteBuffer* buffer) {
	/*struct tm oResult;
	localtime_r(&value,&oResult);
	// Datetime format DD.MM.YYYY HH:MM:SS
	const boost::format oDateTimeFormatter("%|02|d.%|02|d.%|04|d %|02|d:%|02|d:%|02|d");
	std::string sDateTime = (boost::format(oDateTimeFormatter)
		%(oResult.tm_mday)
		%(oResult.tm_mon+1)
		%(oResult.tm_year+1900)
		%(oResult.tm_hour)
		%(oResult.tm_min)
		%(oResult.tm_sec)
	).str();*/
	boost::uint64_t nIntegerForm = value;
	encodeNumberType(nIntegerForm, DATETIME_TAG, buffer);
}

time_t StdDataTypesCoder::decodeDateTime(ByteBuffer* buffer) {
	if(checkTag(buffer, DATETIME_TAG) ) {
		boost::uint64_t* nIntegerForm = (boost::uint64_t*) buffer->getDataAtPos();
		// time_t* valueToDecode = (time_t*) buffer->getDataAtPos();
		buffer->seek(sizeof(boost::uint64_t));
		return (time_t)OSEndianTools::network_to_host( *nIntegerForm );
	}
	else {
		return 0;
	}
}

void StdDataTypesCoder::encodeByteArray(const std::vector<char>& value, ByteBuffer* buffer)  {
	encodeTag ( BYTE_ARRAY_TAG, buffer );
	encodeFloatingLength( value.size(), buffer );
	for(std::vector<char>::const_iterator it = value.begin() ; it!= value.end(); it++) {
		buffer->append((unsigned char) *it );
	}
}

void StdDataTypesCoder::decodeByteArray(std::vector<char>& toValue, ByteBuffer* buffer)  {
	toValue.clear();
	if( checkTag(buffer, BYTE_ARRAY_TAG ) ) {
		unsigned int nLen = decodeFloatingLength( buffer );
		for(unsigned int i=0; i<nLen; i++) {
			toValue.push_back ( *((char*)buffer->getDataAtPos()) );
			buffer->seek(1);
		}
	}
}

void StdDataTypesCoder::encodeUByteArray(const std::vector<unsigned char>& value, ByteBuffer* buffer)  {
	encodeTag ( UBYTE_ARRAY_TAG, buffer );
	encodeFloatingLength( value.size(), buffer );
	for(std::vector<unsigned char>::const_iterator it = value.begin() ; it!= value.end(); it++) {
		buffer->append(*it );
	}
}

void StdDataTypesCoder::decodeUByteArray(std::vector<unsigned char>& toValue, ByteBuffer* buffer)  {
	toValue.clear();
	if( checkTag(buffer, UBYTE_ARRAY_TAG ) ) {
		unsigned int nLen = decodeFloatingLength( buffer );
		for(unsigned int i=0; i<nLen; i++) {
			toValue.push_back ( *(buffer->getDataAtPos()) );
			buffer->seek(1);
		}
	}
}

void StdDataTypesCoder::encodeInt16Array(const std::vector<short>& value, ByteBuffer* buffer)  {
	encodeArrayOfNumbersType(value, INT16_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::decodeInt16Array(std::vector<short>& toValue, ByteBuffer* buffer)  {
	decodeArrayOfNumbersType(toValue, INT16_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::encodeUInt16Array(const std::vector<unsigned short>& value, ByteBuffer* buffer)  {
	encodeArrayOfNumbersType(value, UINT16_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::decodeUInt16Array(std::vector<unsigned short>& toValue, ByteBuffer* buffer)  {
	decodeArrayOfNumbersType(toValue, UINT16_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::encodeInt32Array(const std::vector<int>& value, ByteBuffer* buffer)  {
	encodeArrayOfNumbersType(value, INT32_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::decodeInt32Array(std::vector<int>& toValue, ByteBuffer* buffer)  {
	decodeArrayOfNumbersType(toValue, INT32_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::encodeUInt32Array(const std::vector<unsigned int>& value, ByteBuffer* buffer)  {
	encodeArrayOfNumbersType(value, UINT32_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::decodeUInt32Array(std::vector<unsigned int>& toValue, ByteBuffer* buffer)  {
	decodeArrayOfNumbersType(toValue, UINT32_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::encodeInt64Array(const std::vector<boost::int64_t>& value, ByteBuffer* buffer)  {
	encodeArrayOfNumbersType(value, INT64_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::decodeInt64Array(std::vector<boost::int64_t>& toValue, ByteBuffer* buffer)  {
	decodeArrayOfNumbersType(toValue, INT64_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::encodeUInt64Array(const std::vector<boost::uint64_t>& value, ByteBuffer* buffer)  {
	encodeArrayOfNumbersType(value, UINT64_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::decodeUInt64Array(std::vector<boost::uint64_t>& toValue, ByteBuffer* buffer)  {
	decodeArrayOfNumbersType(toValue, UINT64_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::encodeFloatArray(const std::vector<float>& value, ByteBuffer* buffer)  {
	encodeArrayOfNumbersType(value, FLOAT_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::decodeFloatArray(std::vector<float>& toValue, ByteBuffer* buffer)  {
	decodeArrayOfNumbersType(toValue, FLOAT_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::encodeDoubleArray(const std::vector<double>& value, ByteBuffer* buffer)  {
	encodeArrayOfNumbersType(value, DOUBLE_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::decodeDoubleArray(std::vector<double>& toValue, ByteBuffer* buffer)  {
	decodeArrayOfNumbersType(toValue, DOUBLE_ARRAY_TAG, buffer);
}

void StdDataTypesCoder::encodeBigIntegerArray(const std::vector<BigInteger>& fromArray, ByteBuffer* buffer) {
	encodeTag ( BIGINTEGER_ARRAY_TAG, buffer );
	encodeFloatingLength( fromArray.size(), buffer );
	for(std::vector<BigInteger>::const_iterator it = fromArray.begin() ; it!= fromArray.end(); it++) {
		encodeBigInteger ( *it, buffer );
	}
}

void StdDataTypesCoder::decodeBigIntegerArray(std::vector<BigInteger>& toArray, ByteBuffer* buffer ) {
	toArray.clear();
	if( checkTag(buffer, BIGINTEGER_ARRAY_TAG ) ) {
		unsigned int nLen = decodeFloatingLength( buffer );
		for(unsigned int i=0; i<nLen; i++) {
			BigInteger sItem;
			decodeBigInteger ( sItem, buffer );
			toArray.push_back(sItem);
		}
	}
}

void StdDataTypesCoder::encodeBigDecimalArray(const std::vector<BigDecimal>& fromArray, ByteBuffer* buffer) {
	encodeTag ( BIGDECIMAL_ARRAY_TAG, buffer );
	encodeFloatingLength( fromArray.size(), buffer );
	for(std::vector<BigDecimal>::const_iterator it = fromArray.begin() ; it!= fromArray.end(); it++) {
		encodeBigDecimal ( *it, buffer );
	}
}

void StdDataTypesCoder::decodeBigDecimalArray(std::vector<BigDecimal>& toArray, ByteBuffer* buffer ) {
	toArray.clear();
	if( checkTag(buffer, BIGDECIMAL_ARRAY_TAG ) ) {
		unsigned int nLen = decodeFloatingLength( buffer );
		for(unsigned int i=0; i<nLen; i++) {
			BigDecimal sItem;
			decodeBigDecimal ( sItem, buffer );
			toArray.push_back(sItem);
		}
	}
}

void StdDataTypesCoder::encodeStringArray(const std::vector<std::string>& value, ByteBuffer* buffer)  {
	encodeTag ( STRING_ARRAY_TAG, buffer );
	encodeFloatingLength( value.size(), buffer );
	for(std::vector<std::string>::const_iterator it = value.begin() ; it!= value.end(); it++) {
		encodeString ( *it, buffer );
	}
}

void StdDataTypesCoder::decodeStringArray(std::vector<std::string>& toValue, ByteBuffer* buffer )  {
	toValue.clear();
	if( checkTag(buffer, STRING_ARRAY_TAG ) ) {
		unsigned int nLen = decodeFloatingLength( buffer );
		for(unsigned int i=0; i<nLen; i++) {
			std::string sItem;
			decodeString(sItem, buffer);
			toValue.push_back(sItem);
		}
	}
}

void StdDataTypesCoder::encodeWStringArray(const std::vector<std::wstring>& value, ByteBuffer* buffer)  {
	encodeTag ( WSTRING_ARRAY_TAG, buffer );
	encodeFloatingLength( value.size(), buffer );
	for(std::vector<std::wstring>::const_iterator it = value.begin() ; it!= value.end(); it++) {
		encodeWString ( *it, buffer );
	}
}

void StdDataTypesCoder::decodeWStringArray(std::vector<std::wstring>& toValue, ByteBuffer* buffer)  {
	toValue.clear();
	if( checkTag(buffer, WSTRING_ARRAY_TAG ) ) {
		unsigned int nLen = decodeFloatingLength( buffer );
		for(unsigned int i=0; i<nLen; i++) {
			std::wstring sItem;
			decodeWString(sItem, buffer);
			toValue.push_back(sItem);
		}
	}
}

void StdDataTypesCoder::encodeCharArray(const std::vector<char>& value, ByteBuffer* buffer)  {
	encodeTag ( CHAR_ARRAY_TAG, buffer );
	encodeFloatingLength( value.size(), buffer );
	for(std::vector<char>::const_iterator it = value.begin() ; it!= value.end(); it++) {
		buffer->append((unsigned char) *it );
	}
}

void StdDataTypesCoder::decodeCharArray(std::vector<char>& toValue, ByteBuffer* buffer)  {
	toValue.clear();
	if( checkTag(buffer, CHAR_ARRAY_TAG ) ) {
		unsigned int nLen = decodeFloatingLength( buffer );
		for(unsigned int i=0; i<nLen; i++) {
			toValue.push_back ( *((char*)buffer->getDataAtPos()) );
			buffer->seek(1);
		}
	}
}

void StdDataTypesCoder::encodeWCharArray(const std::vector<wchar_t>& value, ByteBuffer* buffer)  {
	encodeTag ( WCHAR_ARRAY_TAG, buffer );
	encodeFloatingLength( value.size(), buffer );
	for(std::vector<wchar_t>::const_iterator it = value.begin() ; it!= value.end(); it++) {
		boost::int16_t valueToEncode = OSEndianTools::host_to_network( (boost::int16_t ) (*it));
		buffer->append( (unsigned char*)&valueToEncode, sizeof(boost::int16_t) );
	}
}

void StdDataTypesCoder::decodeWCharArray(std::vector<wchar_t>& toValue, ByteBuffer* buffer)  {
	toValue.clear();
	if( checkTag(buffer, WCHAR_ARRAY_TAG ) ) {
		unsigned int nLen = decodeFloatingLength( buffer );
		for(unsigned int i=0; i<nLen; i++) {
			short* pSource = (short*)buffer->getDataAtPos();
			boost::int16_t ucs2Char = OSEndianTools::network_to_host(*pSource);
			toValue.push_back((wchar_t)ucs2Char);
			buffer->seek(2);
		}
	}
}

void StdDataTypesCoder::encodeBooleanArray(const std::vector<bool>& value, ByteBuffer* buffer)  {
	encodeTag ( BOOLEAN_ARRAY_TAG, buffer );
	encodeFloatingLength( value.size(), buffer );
	for(std::vector<bool>::const_iterator it = value.begin() ; it!= value.end(); it++) {
		buffer->append((unsigned char) *it );
	}
}

void StdDataTypesCoder::decodeBooleanArray(std::vector<bool>& toValue, ByteBuffer* buffer)  {
	toValue.clear();
	if( checkTag(buffer, BOOLEAN_ARRAY_TAG ) ) {
		unsigned int nLen = decodeFloatingLength( buffer );
		for(unsigned int i=0; i<nLen; i++) {
			toValue.push_back ( *((bool*)buffer->getDataAtPos()) );
			buffer->seek(1);
		}
	}
}

void StdDataTypesCoder::encodeDateTimeArray(const std::vector<time_t>& value, ByteBuffer* buffer)  {
	encodeTag ( DATETIME_ARRAY_TAG, buffer );
	encodeFloatingLength( value.size(), buffer );
	for(std::vector<time_t>::const_iterator it = value.begin() ; it!= value.end(); it++) {
		boost::uint64_t valueToEncode = OSEndianTools::host_to_network( (boost::uint64_t) *it );
		buffer->append( (unsigned char*)&valueToEncode, sizeof(boost::uint64_t) );
	}

}

void StdDataTypesCoder::decodeDateTimeArray(std::vector<time_t>& value, ByteBuffer* buffer)  {
	value.clear();
	if( checkTag(buffer, DATETIME_ARRAY_TAG ) ) {
		unsigned int nLen = decodeFloatingLength( buffer );
		for(unsigned int i=0; i<nLen; i++) {
			boost::uint64_t* valueToDecode = (boost::uint64_t*) buffer->getDataAtPos();
			buffer->seek(sizeof(boost::uint64_t));
			value.push_back (  (time_t)(OSEndianTools::network_to_host( *valueToDecode )) );
		}
	}

}

void StdDataTypesCoder::encodeObjectArray(const unsigned int arraySize, ByteBuffer* buffer)  {
	encodeTag ( OBJECT_ARRAY_TAG, buffer );
	encodeFloatingLength( arraySize, buffer );
}

unsigned int StdDataTypesCoder::decodeObjectArray( ByteBuffer* buffer )  {
	if(checkTag ( buffer, OBJECT_ARRAY_TAG ))
		return decodeFloatingLength( buffer );
	else
		return 0;
}

unsigned int StdDataTypesCoder::encodeObjectListBegin( ByteBuffer* buffer )  {
	encodeTag ( OBJECT_LIST_TAG, buffer );
	unsigned int nBeginPos = buffer->getLen();
	encodeFixedLength(0, buffer); // Reserve space for count
	return nBeginPos;
}

unsigned int StdDataTypesCoder::decodeObjectListBegin ( ByteBuffer* buffer )  {
	if( checkTag ( buffer, OBJECT_LIST_TAG  ) ) {
		return decodeFixedLength(buffer);
	}
	else
		return 0;
}

void StdDataTypesCoder::encodeObjectListEnd ( unsigned int nBeginPos, unsigned int nCount, ByteBuffer* buffer )  {
	unsigned int nCurLen = buffer->getLen();
	buffer->setLen(nBeginPos);
	encodeFixedLength(nCount, buffer);
	buffer->setLen(nCurLen);
}

void StdDataTypesCoder::decodeObjectListEnd(ByteBuffer* buffer)  {
}

void StdDataTypesCoder::encodeObjectMap(const unsigned int mapSize, ByteBuffer* buffer)  {
	encodeTag ( OBJECT_MAP_TAG, buffer );
	encodeFloatingLength( mapSize, buffer );
}

unsigned int StdDataTypesCoder::decodeObjectMap(ByteBuffer* buffer)  {
	if(checkTag ( buffer, OBJECT_MAP_TAG ))
		return decodeFloatingLength( buffer );
	else
		return 0;
}


void StdDataTypesCoder::encodeNull(ByteBuffer* buffer)  {
	encodeTag(NULL_TAG, buffer );
}

bool StdDataTypesCoder::decodeNull(ByteBuffer* buffer)  {
	int nCurPos = buffer->getPos();
	if(decodeTag(buffer)!=NULL_TAG) {
		buffer->setPos(nCurPos);
		return false;
	}
	else
		return true;
}

void StdDataTypesCoder::encodeStructureDataTypeBegin(ByteBuffer* buffer)  {
	encodeTag(STRUCTURE_TYPE_BEGIN_TAG, buffer);
}

bool StdDataTypesCoder::decodeStructureDataTypeBegin(ByteBuffer* buffer)  {
	return checkTag ( buffer, STRUCTURE_TYPE_BEGIN_TAG );
}

void StdDataTypesCoder::encodeStructureDataTypeEnd(ByteBuffer* buffer)  {
	encodeTag(STRUCTURE_TYPE_END_TAG, buffer);
}

void StdDataTypesCoder::decodeStructureDataTypeEnd(ByteBuffer* buffer)  {
	int nTag = decodeTag ( buffer );
	std::string sPartName;
	while (nTag == NAMED_DATA_PART_TAG) {
		// decodeStringValue(sPartName, buffer);
		buffer->seek(sizeof(boost::uint32_t));
		unsigned int nPartSize = decodeFixedLength(buffer);
		buffer->seek( nPartSize );
		nTag = decodeTag ( buffer );
	}

	if(nTag!= STRUCTURE_TYPE_END_TAG)
		throw DataTypesCoderException( (boost::format( "Unexpected end of structure received. Found tag: %1%. Expected: %2%") %nTag %STRUCTURE_TYPE_END_TAG).str());
}

unsigned int StdDataTypesCoder::encodeNamedDataPartBegin ( const std::string& sPartName, ByteBuffer* buffer )  {
	encodeTag ( NAMED_DATA_PART_TAG, buffer );
	// encodeStringValue ( sPartName, buffer );
	boost::uint32_t valueToEncode = OSEndianTools::host_to_network(m_poGrammar->registerGrammarElement(sPartName));
	buffer->append( (unsigned char*)&valueToEncode, sizeof(boost::uint32_t) );

	unsigned int nBeginPos = buffer->getLen();
	encodeFixedLength(0, buffer); // reserve space for len
	return nBeginPos;
}

unsigned int StdDataTypesCoder::decodeNamedDataPartBegin ( const std::string& sPartName, ByteBuffer* buffer)  {
	unsigned int nResult = 0;
	unsigned int nCurPos = buffer->getPos();
	bool bTryToFindPart = true;
	std::string sFoundPartName;
	while ( bTryToFindPart ) {
		int nTag = decodeTag(buffer);
		if ( nTag == NAMED_DATA_PART_TAG ) {
			// decodeStringValue(sFoundPartName, buffer);
			boost::uint32_t* valueToDecode = (boost::uint32_t*) buffer->getDataAtPos();
			buffer->seek(sizeof(boost::uint32_t));
			sFoundPartName = m_poGrammar->findGrammarElement(OSEndianTools::network_to_host( *valueToDecode ));

			nResult = buffer->getPos();
			unsigned int nPartSize = decodeFixedLength(buffer);
			if(sFoundPartName == sPartName) {
				bTryToFindPart = false;
			}
			else {
				// Skip the current part
				nResult = 0;
				buffer->seek ( nPartSize );
			}
		}
		else {
			buffer->setPos( nCurPos );
			bTryToFindPart = false;
		}
	}

	if( nResult == 0 ) {
		throw DataTypesCoderException( (boost::format( "Unable to found named part '%1%' in the byte stream.") %sPartName).str());
	}

	return nResult;
}

void StdDataTypesCoder::encodeNamedDataPartEnd ( const unsigned int nBeginPos, ByteBuffer* buffer )  {
	unsigned int nCurLen = buffer->getLen();
	unsigned int nSize = nCurLen - ( nBeginPos + sizeof(unsigned int) );
	buffer->setLen(nBeginPos);
	encodeFixedLength(nSize, buffer);
	buffer->setLen(nCurLen);
}

void StdDataTypesCoder::decodeNamedDataPartEnd ( const unsigned int nBeginPos, ByteBuffer* buffer )  {
	// Skipping an additional data in the part
	unsigned int nCurPos = buffer->getPos();
	buffer->setPos(nBeginPos);
	unsigned int nPartSize = decodeFixedLength(buffer);
	buffer->setPos(nCurPos);
	unsigned int nEstiminateDataSize = (nBeginPos + sizeof(unsigned int) + nPartSize ) - nCurPos;
	buffer->seek ( nEstiminateDataSize );
}

void StdDataTypesCoder::encodeEnumDataType(const int nSelectedIndex, ByteBuffer* buffer)  {
	encodeTag ( ENUM_TYPE_TAG, buffer );
	encodeFixedLength ( (unsigned int)nSelectedIndex, buffer );
}

int StdDataTypesCoder::decodeEnumDataType(ByteBuffer* buffer)  {
	if(checkTag(buffer, ENUM_TYPE_TAG)) {
		return (int)decodeFixedLength(buffer);
	}
	else
		return 0;
}

void StdDataTypesCoder::encodeExceptionDataTypeBegin(ByteBuffer* buffer)  {
	encodeTag ( EXCEPTION_TYPE_BEGIN_TAG, buffer );
}

bool StdDataTypesCoder::decodeExceptionDataTypeBegin(ByteBuffer* buffer)  {
	return checkTag(buffer, EXCEPTION_TYPE_BEGIN_TAG);
}

void StdDataTypesCoder::encodeExceptionDataTypeEnd(ByteBuffer* buffer)  {
	encodeTag ( EXCEPTION_TYPE_END_TAG, buffer );
}

void StdDataTypesCoder::decodeExceptionDataTypeEnd(ByteBuffer* buffer)  {
	checkTag(buffer, EXCEPTION_TYPE_END_TAG, true);
}

unsigned int StdDataTypesCoder::encodeEncryptedDataTypeBegin( ByteBuffer* buffer )  {
	encodeTag ( ENCRYPTED_DATA_TAG, buffer );
	int nBeginPos = buffer->getLen();
	encodeFixedLength(0, buffer); // reserve space for len
	return nBeginPos;
}

unsigned int StdDataTypesCoder::decodeEncryptedDataTypeBegin( ByteBuffer* buffer )  {
	checkTag ( buffer, ENCRYPTED_DATA_TAG, true );
	return decodeFixedLength(buffer);
}

void StdDataTypesCoder::encodeEncryptedDataTypeEnd( const unsigned int nBeginPos, ByteBuffer* buffer )  {
	unsigned int nCurLen = buffer->getLen();
	unsigned int nSize = nCurLen - ( nBeginPos + sizeof(unsigned int) );
	buffer->setLen(nBeginPos);
	encodeFixedLength(nSize, buffer);
	buffer->setLen(nCurLen);
}

void StdDataTypesCoder::decodeEncryptedDataTypeEnd( ByteBuffer* buffer )  {
}

void StdDataTypesCoder::encodeObjectType ( const std::string& className, ByteBuffer* buffer )  {
	encodeTag ( OBJECT_TYPE_TAG, buffer );
	// encodeStringValue ( className, buffer );
	boost::uint32_t valueToEncode = OSEndianTools::host_to_network(m_poGrammar->registerGrammarElement(className));
	buffer->append( (unsigned char*)&valueToEncode, sizeof(boost::uint32_t) );
}

void StdDataTypesCoder::decodeObjectType(std::string& className, ByteBuffer* buffer )  {
	checkTag ( buffer, OBJECT_TYPE_TAG, true );
	// decodeStringValue (className, buffer );
	boost::uint32_t* valueToDecode = (boost::uint32_t*) buffer->getDataAtPos();
	buffer->seek(sizeof(boost::uint32_t));
	className = m_poGrammar->findGrammarElement(OSEndianTools::network_to_host( *valueToDecode ));
}

unsigned int StdDataTypesCoder::encodeChoiceDataTypeBegin(ByteBuffer* buffer)  {
	encodeTag ( CHOICE_TYPE_TAG, buffer );
	int nBeginPos = buffer->getLen();
	encodeFixedLength(0, buffer); // reserve space for len
	return nBeginPos;
}

unsigned int StdDataTypesCoder::decodeChoiceDataTypeBegin(ByteBuffer* buffer)  {
	checkTag ( buffer, CHOICE_TYPE_TAG, true );
	int nBeginPos = buffer->getPos();
	decodeFixedLength(buffer);
	return nBeginPos;
}

void StdDataTypesCoder::encodeChoiceSelectedField(const std::string& selectedField, ByteBuffer* buffer)  {
	// encodeStringValue ( selectedField, buffer );
	boost::uint32_t valueToEncode = OSEndianTools::host_to_network(m_poGrammar->registerGrammarElement(selectedField));
	buffer->append( (unsigned char*)&valueToEncode, sizeof(boost::uint32_t) );
}

void StdDataTypesCoder::decodeChoiceSelectedField(std::string& selectedField, ByteBuffer* buffer)  {
	// decodeStringValue(selectedField, buffer);
	boost::uint32_t* valueToDecode = (boost::uint32_t*) buffer->getDataAtPos();
	buffer->seek(sizeof(boost::uint32_t));
	selectedField = m_poGrammar->findGrammarElement(OSEndianTools::network_to_host( *valueToDecode ));
}

void StdDataTypesCoder::encodeChoiceDataTypeEnd(const unsigned int nBeginPos, ByteBuffer* buffer)  {
	unsigned int nCurLen = buffer->getLen();
	unsigned int nSize = nCurLen - ( nBeginPos + sizeof(unsigned int) );
	buffer->setLen(nBeginPos);
	encodeFixedLength(nSize, buffer);
	buffer->setLen(nCurLen);
}

void StdDataTypesCoder::decodeChoiceDataTypeEnd(const unsigned int nBeginPos, ByteBuffer* buffer)  {
	// Skipping an additional data in the part
	unsigned int nCurPos = buffer->getPos();
	buffer->setPos(nBeginPos);
	unsigned int nPartSize = decodeFixedLength(buffer);
	buffer->setPos(nCurPos);
	unsigned int nEstiminateDataSize = (nBeginPos + sizeof(unsigned int) + nPartSize ) - nCurPos;
	buffer->seek ( nEstiminateDataSize );
}

void StdDataTypesCoder::encodeGrammar(ByteBuffer* buffer) {
	m_poGrammar->serialize(this, NULL, buffer);
}

void StdDataTypesCoder::decodeGrammar(ByteBuffer* buffer) {
	m_poGrammar->deserialize(this, NULL, buffer);
}

void StdDataTypesCoder::reset() {
	m_poGrammar->reset();
}

}
